#include"inode.h"
#include"thread.h"
#include"memory.h"
#include"fs.h"
#include"debug.h"
#include"string.h"

static struct winode* find_open_inode(struct partition*,uint32_t);
static void load_pure_inode(struct inode*,struct partition*,uint32_t);

struct winode* open_inode(struct partition* part,uint32_t i_no){
	struct winode* inode;
	uint32_t* page_dir_bak;	
	task_tcb* curr;
	inode=find_open_inode(part,i_no);
	if(inode==NULL){
		curr=current_thread();
		page_dir_bak=curr->page_dir;
		//open inode list should in kernal space so all process can share
		curr->page_dir=NULL;
		inode=sys_malloc(sizeof(struct winode));
		ASSERT(inode!=NULL);
		curr->page_dir=page_dir_bak;
		load_pure_inode(&inode->pure_node,part,i_no);
		inode->open_cnt=1;
		list_append(&part->open_inode_list,&inode->tag);
	}else{
		inode->open_cnt++;
	}
	return inode;
}

void close_inode(struct winode* inode){
	task_tcb* curr;
	uint32_t* page_dir_bak;
	if(--inode->open_cnt==0){
		list_remove(&inode->tag);
		curr=current_thread();
		page_dir_bak=curr->page_dir;
		curr->page_dir=NULL;
		sys_free(inode);
		curr->page_dir=page_dir_bak;
	}
}

static void load_pure_inode(struct inode* pure_inode,struct partition* part,uint32_t i_no){
	ASSERT(part->sb);
	char buffer[SECTOR_SIZE];
	uint32_t inode_start_lba=part->sb->inode_table_lba+part->sb->pure_inode_size*i_no/SECTOR_SIZE;
	uint32_t inode_index=part->sb->pure_inode_size*i_no%SECTOR_SIZE;
	ide_read(part->disk,buffer,inode_start_lba,1);
	memcpy(pure_inode,buffer+inode_index,part->sb->pure_inode_size);
}


static struct winode* find_open_inode(struct partition* part,uint32_t i_no){
	list_elem* p;
	struct winode* inode;
	if(!list_empty(&part->open_inode_list)){
		for(p=part->open_inode_list.head.next;p!=&part->open_inode_list.tail;p=p->next){
			inode=elem2entry(struct winode,tag,p);
			if(inode->pure_node.i_no==i_no){
				return inode;
			}		
		}
	}
	return NULL;
}

void fs_bitmap_sync(struct partition* part,uint32_t bit_idx,bitmap_type bt){
	uint32_t off_sec;
	off_sec=bit_idx/BITS_PER_SEC;
	uint8_t* bitmap_off;
	
	bitmap_off=bt==FS_BLOCK_BITMAP ? \
		  part->block_bitmap.bits+off_sec*SECTOR_SIZE : \
		  part->inode_bitmap.bits+off_sec*SECTOR_SIZE; 
	ide_write(part->disk,(char*)bitmap_off, \
			bt==FS_BLOCK_BITMAP ? \
			part->sb->block_bitmap_lba+off_sec : \
			part->sb->inode_bitmap_lba+off_sec,1);

}

int32_t fs_bitmap_alloc(struct partition* part,bitmap_type bt){
	bitmap* map;
	int32_t bit_idx;
	map=bt==FS_BLOCK_BITMAP ? &part->block_bitmap : &part->inode_bitmap;
	bit_idx=bitmap_scan(map,1);
	if(bit_idx<0){
		return -1;
	}
	bitmap_set(map,bit_idx,1);
	return bit_idx;
}

void inode_sync(struct partition* part,struct winode* wi,char* iobuffer){
	uint32_t sec_idx;
	struct inode* i_table;
	sec_idx=wi->pure_node.i_no*sizeof(struct inode)/SECTOR_SIZE;
	ide_read(part->disk,iobuffer,part->sb->inode_table_lba+sec_idx,1);
	i_table=(struct inode*)iobuffer;	
	memcpy(&i_table[wi->pure_node.i_no%INODE_PER_SECTOR],&wi->pure_node,sizeof(struct inode));
	ide_write(part->disk,iobuffer,part->sb->inode_table_lba+sec_idx,1);
}

//open inode must share to all task so it use kernal memory
struct winode* create_inode(struct partition* part,uint32_t i_no){
	struct winode* wi;
	uint32_t* page_dir_bak;	
	task_tcb* curr;
	curr=current_thread();
	page_dir_bak=curr->page_dir;
	curr->page_dir=NULL;	
	wi=sys_malloc(sizeof(struct winode));
	wi->pure_node.i_no=i_no;
	curr->page_dir=page_dir_bak;
	list_append(&part->open_inode_list,&wi->tag);
	return wi;
}

bool release_inode(struct partition* part,int32_t i_no,char* iobuffer){
	struct winode* wi;
	uint32_t block_lba;	
	uint32_t* pblocks;
	uint32_t b_idx=0;

	wi=open_inode(part,i_no);
	if(wi==NULL){
		return false;
	}
	pblocks=wi->pure_node.blocks;
	//free direct block
	while(b_idx<INODE_BLOCK_CNT-1){
		block_lba=pblocks[b_idx];
		if(block_lba){
			bitmap_set(&part->block_bitmap,block_lba-part->sb->free_block_lba,0);
			fs_bitmap_sync(part,block_lba-part->sb->free_block_lba,FS_BLOCK_BITMAP);
		}
		b_idx++;
	}
	block_lba=pblocks[INODE_BLOCK_CNT-1];
	
	//free block in indirect block & indirect block
	if(block_lba){
		//free indirect block
		bitmap_set(&part->block_bitmap,block_lba-part->sb->free_block_lba,0);
		fs_bitmap_sync(part,block_lba-part->sb->free_block_lba,FS_BLOCK_BITMAP);
		b_idx=0;
		pblocks=(uint32_t*)iobuffer;
		ide_read(part->disk,iobuffer,block_lba,1);
		while(b_idx<BLOCK_INDEX_CNT_PER_SECTOR){
			block_lba=pblocks[b_idx];
			if(block_lba){
				//free block in indirect block
				bitmap_set(&part->block_bitmap,block_lba-part->sb->free_block_lba,0);
				fs_bitmap_sync(part,block_lba-part->sb->free_block_lba,FS_BLOCK_BITMAP);
			}
			b_idx++;
		}
	}
	//free inode
	bitmap_set(&part->inode_bitmap,i_no,0);
	fs_bitmap_sync(part,i_no,FS_INODE_BITMAP);
	close_inode(wi);
	sys_free(wi);
	return true;
}

void node_all_block(struct partition* part,struct inode* pure_inode,uint32_t* all_blocks,char* buffer){
	uint32_t b_idx=0;
	uint32_t* p_blocks;

	ASSERT(all_blocks);
	//collect direct block
	while(b_idx<INODE_BLOCK_CNT-1){
		if(pure_inode->blocks[b_idx]!=0){
			all_blocks[b_idx]=pure_inode->blocks[b_idx];
		}
		b_idx++;
	}
	//try collect indirect block
	if(pure_inode->blocks[INODE_BLOCK_CNT-1]!=0){
		p_blocks=(uint32_t*)buffer;
		ide_read(part->disk,buffer,pure_inode->blocks[b_idx],1);
		b_idx=0;
		while(b_idx<BLOCK_INDEX_CNT_PER_SECTOR){
			if(p_blocks[b_idx]!=0){
				all_blocks[b_idx]=p_blocks[b_idx];
			}
			b_idx++;
		}
	}
}
